/**
 * @file gprs.c
 * @Synopsis  电子价签的gprs模块控制程序，主要包括AT解析，发送
 *            GPRS数据,当前GPRS模块没有用线程独立管理，直接提供
 *            API让上层应用访问，所以设备状态由应用层管理。
 * @author SWFighter
 * @version 1.0.0V
 * @date 2018-07-13
 */
#include "gprs.h"
#include "comm.h"
#include "stdlib.h"
#include "string.h"
#include "espressif/upgrade.h"
#include "appconfig.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "upgrade.h"
#include "sysmanage.h"


/*
#ifdef USEING_DEBUG_GPRS
#define printf       printf
#else
#define printf(...)       
#endif
*/

static u8_t gsmType = 0;

static u8_t gsm_status = 0;

static u8_t* gsm_status_get(void)
{
	return &gsm_status;
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  gsm设置gprs链接服务器
 */
/* ----------------------------------------------------------------------------*/
static void gsm_set_gprs_conn(void)
{
	u8_t *status;

	status = gsm_status_get();
	(*status) |= GSM_IS_CONN;//GSM 联网
	printf("GSM CONN GPRS OK\n");
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  gsm设置gprs退出服务器链接
 */
/* ----------------------------------------------------------------------------*/
static void gsm_set_gprs_exit(void)
{
	u8_t *status;

	status = gsm_status_get();
	(*status) &= ~GSM_IS_CONN;//GSM 联网
	printf("GSM EXIT GPRS !!!\n");
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  gsm模块联网状态
 *
 * @Returns  RT_TRUE 已经链接  RT_FALST 链接断开
 */
/* ----------------------------------------------------------------------------*/
static BOOL gsm_link_status(void)
{
	u8_t *status;

	status = gsm_status_get();
	if((*status) & GSM_IS_CONN)//如果是链接服务器的
	{
		return TRUE;
	}
	return FALSE;
}


u8_t gprs_url[32]="118.184.176.34";
static int host_socket;
/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  链接服务器url
 *
 * @Param cmd 0断开当前链接 1链接服务器
 * @Param url 服务器的url
 * @Param port 服务器端口
 * @Param wait 链接等待时间
 *
 * @Returns  0 正常 -1 异常 
 */
/* ----------------------------------------------------------------------------*/
s8_t gsm_gprs_connect(int cmd,char *url,int port,int wait)
{
  s16_t result =-1;
  u8_t *recv_buf,*recv_head;

	//如果已经链接了网络则不需要做任何处理
	if(cmd == 1)
	{
		if(gsm_link_status() == TRUE)//如果已经链接
		{
			printf("GSM IS CONN GPRS\n");
			return 0;
		}
	}

  // 断开连接
  if(cmd == 0)
  {
		close(host_socket);
  }
  else if(cmd == 1)//建立链接
  {    
		host_socket = socket(PF_INET, SOCK_STREAM, 0);
		if (-1 == host_socket) 
		{
			close(host_socket);
			vTaskDelay(1000 / portTICK_RATE_MS);
			printf("ESP8266 TCP client task > socket fail!\n");
			result = -1;
		}
		else //链接成功
		{
			struct sockaddr_in remote_ip;			
			struct hostent *host = NULL;

			host = gethostbyname(url);
			if(host == NULL)
			{
				close(host_socket);
				gsm_set_gprs_exit();//设置GPRS断网
				result = -1;
				return result;
			}
			printf("Get Host Name %s IP: %s\n", host->h_name,inet_ntoa(*((struct in_addr *)host->h_addr)));

			result = 0;
			bzero(&remote_ip, sizeof(struct sockaddr_in));
			remote_ip.sin_family = AF_INET;
			remote_ip.sin_addr.s_addr = inet_addr(inet_ntoa(*((struct in_addr *)host->h_addr)));
			remote_ip.sin_port = htons(port);
			if (0 != connect(host_socket, (struct sockaddr *)(&remote_ip), sizeof(struct
			sockaddr))) 
			{
				close(host_socket);
				vTaskDelay(1000 / portTICK_RATE_MS);
				printf("ESP8266 TCP client task > connect fail!\n");
				result = -1;
			}
			else
			{
				printf("ESP8266 TCP client task > connect ok!\n");	
			}
		}
		
    if(result >= 0)
    {
    	gsm_set_gprs_conn();//设置GPRS链接状态
      result = 0;
    }
    else
    {
    	gsm_set_gprs_exit();//设置GPRS断网
      result = -1;
    }
  }

  //free(recv_head);

  return result;
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  GPRS链接服务器，链接失败自动重连3次
 *
 * @Param fun  gprs链接服务器函数
 * @Param cmd  AT命令
 * @Param url  服务器url
 * @Param port 服务器端口
 * @Param wait 超时等待时间
 *
 * @Returns   
 */
/* ----------------------------------------------------------------------------*/
STATUS gprs_connet_resend(s8_t (*fun)(int ,char *,int ,int),int cmd,char *url,int port,int wait)
{
	u8_t run;
	s8_t result;
	
	run = 1;
	do
	{
		result = fun(cmd,url,port,wait);
		if(result == 0)
		{
			return OK;
		}
	}while(run--);

	return FAIL;
}

#include "esp_common.h"
//#define REG_READ(r)   (*(volatile u32_t *)(r))
#define WDEV_NOW()    REG_READ(0x3ff20c00)
static uint32 tick_now2 = 0;
static u8_t tcpFlag = 0;   //tcp处理标记 1 处理中 0 没有处理
static u32_t timeSec = 1;


void hw_test_timer_cb(void)
{
	static u16_t j=0;

	j++;
	if((WDEV_NOW() - tick_now2) >= 100)
	{
		tick_now2 = WDEV_NOW();
		printf("%d %d\r\n",timeSec++,j);
		j = 0;
		if(timeSec == 30)
		{
			if(tcpFlag == 1)//tcp还在处理
			{
				u16_t failNum = 0;
				u32_t value;
				
				printf("Tcp Porcess Outtime \r\n");
				value = load_system_event();
				value |= EVT_TCP_EXP;
				save_system_event(value);//标记TCP异常

				failNum = load_bkp_askfail_num();//失败次数
				failNum++;
				if(failNum <= 2)//前两次
				{
					save_bkp_askfail_num(failNum);
					system_restart();
				}
				else
				{
					save_bkp_askfail_num(failNum);
					set_rtc_system_event_bit(EVT_SERVER_NOACK,TRUE);//服务器异常
					system_restart();
				}
			}
		}
	}
}



void tcp_outtime_timer_init(void)
{
	hw_timer_init(1,1);
	hw_timer_set_func(hw_test_timer_cb);
	hw_timer_arm(1000000);
	timeSec = 0;
}


void tcp_entry_process(void)
{
	timeSec = 0;
	tcpFlag = 1;
	tcp_outtime_timer_init();//处理TCP超时
}

void tcp_exit_process(void)
{
	tcpFlag = 0;
	hw_timer_cmd(0);
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis 用透传模式发送GPRS数据 
 *
 * @Param sBuf  发送数据缓存
 * @Param ssize 发送数据缓存大小
 * @Param rBuf  接收数据缓存
 * @Param rsize 接收数据缓存大小
 * @Param delay 发送后等待接受数据的超时时间
 *
 * @Returns  0 接收正常 -1异常 
 */
/* ----------------------------------------------------------------------------*/
s8_t send_tcp_data(u8_t *sBuf,u16_t ssize,u8_t *rBuf,u16_t *rlen,u16_t delay)
{
  u8_t *head;
  u16_t len=0;
  u16_t RSzie;
	u16_t rsize;
	int	flags;

	rsize = (*rlen);
  head = rBuf;

  //发送数据
  printf("%d>>>",ssize);
  show_hex_string(sBuf,ssize);
  write(host_socket,sBuf,ssize);
  while(delay--)
  {
    RSzie = read(host_socket,head,rsize);
    if(RSzie == 0)//没有收到数据
    {
			if((len != 0) && ((head - rBuf) == (len)))//服务器发送的长度
			{
				break;
			}
			vTaskDelay(1 / portTICK_RATE_MS);
    }
    else//接受到数据
    {
      head += RSzie;
      if((head - rBuf) >= rsize)//收到数据
      {
      	len = rsize;
        printf("recv data len=%d\n",len); 
        break;
      }
			if(((head-rBuf)>=2)&&(len == 0))//只解析一次服务器返回的数据长度
			{
        len = rBuf[0];
        len <<= 8;
        len += rBuf[1];
				printf("len=%d\n",len); 
				len += 2;//TCP包长度
				if(len > rsize)//服务器返回的数据比内存还大丢弃
				{
					printf("sever ack len > recv size!!!\n");
					len = rsize;
				}
			}
    }
  }
  if(delay <= 0)
  {
    return -1;
  }

	(*rlen) = len;
  printf("%d<<<",(len));
  show_hex_string(rBuf, len);

	if(len <= 0)
	{
		return -1;
	}
  return 0;
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis    GPRS发送数据，发送失败自动重发三次
 *
 * @Param sBuf  发送缓存
 * @Param ssize 发送大小
 * @Param rBuf  接收缓存
 * @Param rlen  接收大小
 * @Param delay 超时时间
 *
 * @Returns   
 */
/* ----------------------------------------------------------------------------*/
s8_t rsend_tcp_data(u8_t *sBuf,u16_t ssize,u8_t *rBuf,u16_t *rlen,u16_t delay)
{
	u8_t run;
	s8_t result;
	
	run = GPRS_RSEND_MAX;
	do
	{
		tcp_entry_process();//tcp处理中
		result = send_tcp_data(sBuf,ssize,rBuf,rlen,delay);
		tcp_exit_process();//tcp处理完成
		if(result == 0)
		{
			return 0;
		}
	}while(run--);

	return -1;
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  解析获取gsm的信号值
 *
 * @Param buf AT+CSQ指令的返回字符串 
 *
 * @Returns   信号质量 0-30
 */
/* ----------------------------------------------------------------------------*/
static s8_t get_gsm_CSQ(u8_t *buf)
{
  s8_t csq;
	
 	csq = 12;
	
  return csq;
} 


#define  GSM_CMD_ERRMAX  5
/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  GSM 模块开机处理 
 *
 * @Param curCsq 获取当前信号值的指针
 * @Param mode 初始化为透传模式 0 cmd  1透传
 *
 * @Returns 执行结果0 正常  -1异常  -2没有SIM卡 
 */
/* ----------------------------------------------------------------------------*/
s8_t gprs_module_start(s8_t *curCsq,u8_t mode)
{
	return 0;
}


/* --------------------------------------------------------------------------*/
/**
 * @Synopsis  GSM 模块关机
 */
/* ----------------------------------------------------------------------------*/
void gprs_module_stop(void)
{	

}


#define pheadbuffer "Connection: close\r\n\
Cache-Control: no-cache\r\n\
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36 \r\n\
Accept: */*\r\n\
Accept-Encoding: gzip,deflate,sdch\r\n\
Accept-Language: zh-CN,zh;q=0.8\r\n\r\n"


void ota_finished_callback(void *arg)
{
	struct upgrade_server_info *update = arg;
	
	if (update->upgrade_flag == true)
	{
		u8_t result;
		
		affirm_mod_finsh(3,&result);//通知服务器升级完成
		os_printf("OTA	Success ! rebooting!\n");
	#ifdef UpgradFormatFileSys
		spiffs_erase_format();//格式化文件系统
	#endif
		system_upgrade_reboot();
	}
	else
	{
		u8_t result;
		
		affirm_mod_finsh(4,&result);//通知服务器升级完成
		os_printf("OTA failed!\n");
		system_restart();
	}
}


/**
 * server_ip: 服务器地址
 * port:服务器端口
 * path:文件路径
 */
void ICACHE_FLASH_ATTR ota_start_Upgrade(const char *server_ip, uint16_t port,const char *path) 
{
    const char* file;
    //获取系统的目前加载的是哪个bin文件
    u8_t userBin = system_upgrade_userbin_check();

    switch (userBin) {

    //如果检查当前的是处于user1的加载文件，那么拉取的就是user2.bin
    case UPGRADE_FW_BIN1:
        file = "user2.4096.new.6.bin";
        break;

        //如果检查当前的是处于user2的加载文件，那么拉取的就是user1.bin
    case UPGRADE_FW_BIN2:
        file = "user1.4096.new.6.bin";
        break;

        //如果检查都不是，可能此刻不是OTA的bin固件
    default:
        os_printf("Fail read system_upgrade_userbin_check! \n\n");
        return;
    }

    struct upgrade_server_info* update =
            (struct upgrade_server_info *) os_zalloc(
                    sizeof(struct upgrade_server_info));
	struct hostent *host = NULL;

	host = gethostbyname(server_ip);
	if(host == NULL)
	{
		return ;
	}
	printf("Get Host Name %s IP: %s\n", host->h_name,inet_ntoa(*((struct in_addr *)host->h_addr)));

	update->sockaddrin.sin_family = AF_INET;
	update->sockaddrin.sin_addr.s_addr = inet_addr(inet_ntoa(*((struct in_addr *)host->h_addr)));
	update->sockaddrin.sin_port = htons(port);
			
    //设置OTA回调函数
    update->check_cb = ota_finished_callback;
    //设置定时回调时间
    //update->check_times = 10000;
    //从 4M *1024 =4096申请内存
    update->url = (uint8 *)os_zalloc(4096);

    //打印下請求地址
    printf("Http Server Address:%s ,port: %d,filePath: %s,fileName: %s \n",
            inet_ntoa((*((struct in_addr *)host->h_addr))), port, path, file);

    //拼接完整的 URL去请求服务器
    sprintf((char*) update->url, "GET /%s%s HTTP/1.1\r\n"
            "Host: %s:%d\r\n"
    "%s"
    "\r\n", path, file, server_ip, port,pheadbuffer);

	printf("update->url=%s\r\n",update->url);
    if (system_upgrade_start(update) == false) {
        os_printf(" Could not start upgrade\n");
        //释放资源
        os_free(update->url);
        os_free(update);
    } else {
        os_printf(" Upgrading...\n");
    }
}


